use anyhow::Error;
use anyhow::Result;

use crate::model::kline::Kline;
use crate::model::kline::KlineValley;
use crate::model::FudaThing;
use crate::model::kline::Ohlcv;
use crate::round4;

use chrono::DateTime;
use chrono::Utc;
use serde::Deserialize;
use serde::Serialize;
use surrealdb::sql::Thing;
use ts_rs::TS;

use super::GoldenRatio;

///
/// 根据策略依据生成的买卖意向单
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct TradeStorder {
    /// 唯一标识符，由 market.type_.symbol 组成唯一标识 e.g. SH.STOCK.600519
    #[ts(as = "Option<FudaThing>")]
    pub id: Option<Thing>,
    pub security_id: String,
    pub kline_id: String,
    pub klts: DateTime<Utc>,

    pub st_volume: i64,
    pub st_price: f64,

    pub data: Option<TradeStorderData>,
}

/// 交易指令数据
#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub enum TradeStorderData {
    /// 波谷反弹买入单
    ValleyReboundBuy(ValleyReboundBuy),
    /// 波谷止损卖出单
    ValleyStopLossSell(ValleyStopLossSell),
    /// 回撤止盈卖出单与PullbackBu与PullbackBuy组成当日做T或者跨日做T
    PullbackSell(PullbackSell),
    /// 回撤接回买入单
    PullbackBuy(PullbackBuy),
    /// 下跌黄金分割位置买
    SharpDropReboundBuy(SharpDropReboundBuy),
    /// 一般止损单
    StopLossSell(StopLossSell),
}

impl TradeStorder {
    pub fn id_from(kline_id: &str) -> String {
        format!("{}", kline_id)
    }

    pub fn id_raw(&self) -> String {
        let id = self.id.clone().unwrap();
        id.id.to_raw()
    }

    pub fn id_date(&self) -> String {
        let input = self.id_raw();

        let parts: Vec<&str> = input.split('.').collect();
        let date_part = parts.last().unwrap_or(&"").to_string();
        date_part
    }
}

///
/// 根据Kline与买单依据比较生成的策略购买意向订单
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct ValleyReboundBuy {
    ///  对应的Valley
    pub valley_id: String,
    /// 反弹低价
    pub rebound_low: f64,
    /// 反弹幅度
    pub rebound_ratio: f64,

    /// 移动止盈/止损价，根据Kline数据改动
    pub stop_price: f64,

    /// 描述
    pub message: String,
}

///
/// 根据Kline与卖单依据比较生成的策略卖出意向订单
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct ValleyStopLossSell {
    /// 对应的Valley
    pub valley_id: String,
    /// 对应的Buy
    pub buyorder_id: String,

    pub message: String,
}

///
/// 根据Kline与卖单依据比较生成的策略卖出意向订单
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct StopLossSell {
    /// 对应的Buy
    pub buyorder_id: String,

    pub message: String,
}

///
/// 回撤止盈卖出单，与PullbackBuy配合做T当日/跨日
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct PullbackSell {
    /// 对应的Valley
    pub valley_id: String,
    /// 对应的Buy
    pub buyorder_id: String,
    pub message: String,
}

///
/// 回撤止盈后接回买入单，与PullbackSell配合做T当日/跨日
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct PullbackBuy {
    /// 对应的Valley
    pub valley_id: String,

    /// 移动止盈/止损价，根据Kline数据改动
    pub stop_price: f64,

    pub message: String,
}

///
/// 下跌黄金位置买单依据
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct SharpDropReboundBuy {
    /// 当前放量Kline
    pub kline: Kline,

    // down left highest kline
    pub dlh_kline: Kline,

    // down left low kline
    pub dll_kline: Kline,

    /// 移动止盈/止损价，根据Kline数据改动
    pub stop_price: f64,

    /// 描述
    pub message: String,
}

impl SharpDropReboundBuy {
    /// 最大跌幅
    pub fn dllow_high_ratio(&self) -> f64 {
        round4((self.dlh_kline.high - self.dll_kline.low) / self.dlh_kline.high)
    }

    /// 最大跌幅靠近哪个黄金分割数
    pub fn dllow_high_phi(&self) -> GoldenRatio {
        let ratio = self.dllow_high_ratio();
        GoldenRatio::nearest_phi(ratio)
    }

    /// 当前跌幅
    pub fn curr_high_ratio(&self) -> f64 {
        round4((self.dlh_kline.high - self.kline.low) / self.dlh_kline.high)
    }

    /// 当前跌幅靠近哪个黄金分割数
    pub fn curr_high_phi(&self) -> GoldenRatio {
        let ratio = self.curr_high_ratio();
        GoldenRatio::nearest_phi(ratio)
    }
}

impl Into<zenoh::value::Value> for ValleyReboundBuy {
    fn into(self) -> zenoh::value::Value {
        match serde_json::to_vec(&self) {
            Ok(bytes) => zenoh::value::Value::from(bytes),
            Err(e) => panic!("Failed to serialize TradeStrategyBuy to JSON: {}", e),
        }
    }
}

/// A wrapper around Vec<TradeStrategyBuy> to implement Into<zenoh::value::Value>
#[derive(Debug, Serialize, Deserialize)]
pub struct TradeStrategyBuyList(pub Vec<ValleyReboundBuy>);

impl From<Vec<ValleyReboundBuy>> for TradeStrategyBuyList {
    fn from(vec: Vec<ValleyReboundBuy>) -> Self {
        TradeStrategyBuyList(vec)
    }
}

impl Into<zenoh::value::Value> for TradeStrategyBuyList {
    fn into(self) -> zenoh::value::Value {
        match serde_json::to_vec(&self.0) {
            Ok(bytes) => zenoh::value::Value::from(bytes),
            Err(e) => panic!("Failed to serialize TradeStrategyBuyList to JSON: {}", e),
        }
    }
}

impl TryFrom<&zenoh::value::Value> for TradeStrategyBuyList {
    type Error = Error;

    fn try_from(value: &zenoh::value::Value) -> Result<Self, Self::Error> {
        let payload = &value.payload;
        let mut bytes = Vec::new();

        for slice in payload.zslices() {
            bytes.extend_from_slice(slice);
        }

        let deserialized: Result<Vec<ValleyReboundBuy>, _> = serde_json::from_slice(&bytes);
        deserialized.map(TradeStrategyBuyList).map_err(Into::into)
    }
}

#[derive(Serialize, Deserialize, Debug, Clone, Default, TS)]
#[ts(export)]
pub struct UpstopReturnBuy {
    pub prev_ohlcv: Ohlcv,
    pub curr_ohlcv: Ohlcv,
}

/// 抄底专用K线与系列最低价均值偏差
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct KlineLowest {
    pub ohlcv: Ohlcv,
    pub rank: i32,
    pub lowest_lows: Vec<f64>,
    /// mean 均值
    pub mean: f64,
    /// variance 方差
    pub variance: f64,
    /// standard deviation 标准差
    pub std_deviation: f64,
    /// 绝对偏差
    pub abs_deviation: f64,
    /// 百分比偏差
    pub pct_deviation: f64,
}

#[cfg(test)]
mod test {
    use crate::model::trade::trade_storder::ValleyReboundBuy;

    #[tokio::test]
    async fn test_security() {
        // Example JSON string that you might receive from SurrealDB
        let data =
            r#"
    {
        "id": {
            "tb": "security",
            "id": {
                "String": "SH.KZZ.security_code"
            }
        },
        "last_updated": "2024-04-24T14:50:32.046238999Z/30201",

        "market": "SH",
        "name": "security_name_abbr",
        "symbol": "security_code",
        "type_": "KZZ"
    }
    "#;

        // Deserialize the JSON string into the `TradeStrategyBuy` struct
        let security: ValleyReboundBuy = serde_json::from_str(data).unwrap();

        // Now you can use `security` as a Rust struct with all the fields filled in
        println!("{:?}", security);
    }
}
